﻿#region Copyright information
// 
// Copyright © 2005-2013 Yongkee Cho. All rights reserved.
// 
// This code is a part of the Biological Object Library and governed under the terms of the
// GNU Lesser General  Public License (LGPL) version 2.1 which accompanies this distribution.
// For more information on the LGPL, please visit http://bol.codeplex.com/license.
// 
// - Filename: GenomicSegment.cs
// - Author: Yongkee Cho
// - Email: yongkeecho@gmail.com
// - Date Created: 2013-01-25 3:25 PM
// - Last Modified: 2013-01-25 3:59 PM
// 
#endregion
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;

namespace BOL.Models.Genome
{
    /// <summary>
    /// Represents a genomic segment.
    /// </summary>
    [DataContract]
    public class GenomicSegment : Range<int>, IGenomicSegment
    {
        #region Public properties

        /// <summary>Gets or sets chromosome.</summary>
        [DataMember]
        public Chromosome Chromosome { get; set; }

        /// <summary>Gets or sets strand direction.</summary>
        [DataMember]
        public Direction Direction { get; set; }

        #endregion

        #region Constructor

        public GenomicSegment(Chromosome chromosome, int from, int to, Direction direction = Direction.Forward)
            : base(from, to)
        {
            if (chromosome == null)
                throw new ArgumentNullException("chromosome");

            Chromosome = chromosome;
        }

        #endregion

        #region Conversion methods

        public static GenomicSegment Parse(string str)
        {
            var match = Regex.Match(str, @"chr(?<chrName>[0-9]+):(?<lowerBound>[0-9]+)-(?<upperBound>[0-9]+)");
            var group = match.Groups;

            var chromosome = new Chromosome(group["chrName"].Value);
            var lowerBound = Int32.Parse(group["lowerBound"].Value);
            var upperBound = Int32.Parse(group["upperBound"].Value);

            return new GenomicSegment(chromosome, lowerBound, upperBound);
        }

        public static bool TryParse(string str, out GenomicSegment result)
        {
            result = default(GenomicSegment);

            try
            {
                result = Parse(str);
            }
            catch
            {
                return false;
            }

            return true;
        }

        #endregion

        #region Public methods

        public bool Contains(IGenomicSegment other)
        {
            if (other == null)
                throw new ArgumentNullException("other");

            return Chromosome.Equals(other.Chromosome) && Contains(other);
        }

        public bool Overlaps(IGenomicSegment other)
        {
            if (other == null)
                throw new ArgumentNullException("other");

            return Chromosome.Equals(other.Chromosome) && Overlaps(other);
        }

        public IGenomicSegment Intersect(IGenomicSegment other)
        {
            if (other == null)
                throw new ArgumentNullException("other");

            var common = base.Intersect(other);

            return (common == null) ? null : new GenomicSegment(Chromosome, common.From, common.To);
        }

        public IGenomicSegment Union(IGenomicSegment other)
        {
            if (other == null)
                throw new ArgumentNullException("other");

            var union = base.Union(other);

            return new GenomicSegment(Chromosome, union.From, union.To);
        }

        #endregion

        #region IComparable, IComparable<IGenomicSegment>, IEquatable<IGenomicSegment> implementation

        public new int CompareTo(object other)
        {
            if (other == null)
                throw new ArgumentNullException("other");

            if (!(other is IGenomicSegment))
                throw new InvalidCastException("The 'other' argument is not an IGenomicSegment object.");

            return CompareTo(other as IGenomicSegment);
        }

        public int CompareTo(IGenomicSegment other)
        {
            if (other == null)
                throw new ArgumentNullException("other");

            return Chromosome.Equals(other.Chromosome) ? base.CompareTo(other) : Chromosome.CompareTo(other.Chromosome);
        }

        public bool Equals(IGenomicSegment other)
        {
            if (other == null)
                throw new ArgumentNullException("other");

            return Chromosome.Equals(other.Chromosome) && base.Equals(other);
        }

        #endregion

        #region Object overriden

        public override int GetHashCode()
        {
            return Chromosome.GetHashCode() ^ base.GetHashCode();
        }

        public override bool Equals(object other)
        {
            if (other == null)
                throw new ArgumentNullException("other");

            if (!(other is GenomicSegment))
                throw new InvalidCastException("The 'other' argument is not a GenomicSegment object.");

            return Equals(other as GenomicSegment);
        }

        public override string ToString()
        {
            return String.Format("{0}:{1}-{2}", Chromosome, From, To);
        }

        #endregion
    }
}
